/*
 * @file iso9660_charset.c
 *
 *    ISO9660 charset management
 */


#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <iconv.h>
#include <string.h>
#include <rtems/seterr.h>

#include "iso9660_dbg.h"
#include "iso9660_charset.h"

int
iso9660_charset_translate(
    iconv_t        handler,
    const char   *src,
    size_t         src_size,
    char         **dst,
    size_t        *dst_size
)
{
  size_t alloc_size = src_size + ISO9660_CHARSET_TRANSLATE_MARGIN;

  size_t inbytesleft = src_size,
         outbytesleft = alloc_size -1;

  char *inbuf, *outbuf;

  *dst = calloc(sizeof(char), src_size);
  if (!*dst)
    rtems_iso9660_error_and_return_minus_one(ENOMEM, "charset_translate: Not enough memory");

  inbuf = (char*)src;
  outbuf = *dst;

  while (1)
  {
    ssize_t conv = iconv(handler, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
    ptrdiff_t conv_size = outbuf - *dst;

    if (conv == -1)
    {
      switch (errno)
      {
        /* Recoverable error */
        case E2BIG:
          alloc_size += ISO9660_CHARSET_TRANSLATE_MARGIN;
          outbytesleft += ISO9660_CHARSET_TRANSLATE_MARGIN;

          *dst = realloc(*dst, alloc_size);

          outbuf = *dst + conv_size;
          break;

        default:
          if (*dst != NULL)
            free(*dst);

          rtems_iso9660_error_and_return_minus_one(errno,
              "charset_translate: Error while translating string");
          break;
      }
    }

    if (!inbytesleft)
      break;
  }

  *outbuf = '\0';

  if (dst_size != NULL)
    *dst_size = (outbuf - *dst);

  return 0;
}

int
iso9660_charset_from_iocharset
(

    const char              *src,
    size_t                    src_size,
    char                    **dst,
    size_t                   *dst_size,
    const char              *dst_charset,
    const iso9660_fs_info_t *fs
)
{
  iconv_t handler;
  int rc;

  handler = iconv_open(fs->mount_options.iocharset, dst_charset);
  if (handler == (iconv_t) -1)
    rtems_iso9660_error_and_return_minus_one(errno,
        "from_iocharset: Failed to open iconv context");

  rc = iso9660_charset_translate(handler, src, src_size, dst, dst_size);
  if (rc)
    rtems_iso9660_error_and_return_minus_one(errno,
        "from_iocharset: Failed to translate string");

  iconv_close(handler);

  return 0;
}

int
iso9660_charset_to_iocharset
(
    const char  *src,
    size_t        src_size,
    char        **dst,
    size_t       *dst_size,
    const char  *src_charset,
    const iso9660_fs_info_t *fs
)
{
  iconv_t handler;
  int rc;

  handler = iconv_open(src_charset, fs->mount_options.iocharset);

  if (handler == (iconv_t) -1)
  {
    printf("errno=%d, handler=%d\n", errno, handler);
    rtems_iso9660_error_and_return_minus_one(errno,
        "from_iocharset: Failed to open iconv context");
  }

  rc = iso9660_charset_translate(handler, src, src_size, dst, dst_size);
  if (rc)
    rtems_iso9660_error_and_return_minus_one(errno,
        "from_iocharset: Failed to translate string");

  iconv_close(handler);
  return 0;
}
